import java.awt.Container;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import processing.core.*;
import monarch.*;

import java.text.DecimalFormat;
import java.util.*;
// import com.google.protobuf.*;
import controlP5.*;

public class VexGui extends PApplet
{

  /**
   * 
   */
  private static final long        serialVersionUID = -9190068818218920892L;

  static String                    MONARCHEXECUABLE = "../monarch-client";

  static String                    MSGSTART         = ">>>";

  static String                    MSGEND           = "<<<";

  byte[]                           msgOutBuffer;

  int                              width            = 640;

  int                              height           = 480;

  CaptureMJPEG                     capture;

  PImage                           next_img         = null;

  Joystick.ControllerState.Builder builder;

  Process                          monarchProcess;

  BufferedReader                   monarchXMPPReader;

  PrintWriter                      monarchXMPPWriter;

  String                           textValue        = "";

  String                           videoPipe        = "/tmp/monarch-video-out.pipe";

  ControlP5                        controlP5;

  Textfield                        email;

  Boolean                          autoConnect      = false;

  String                           xmppPassword;

  String                           xmppEmail;

  Button                           btnFwd;

  Button                           rosterRefresh;

  Textlabel                        netInfo;

  PFont                            fontA;

  DecimalFormat                    df;

  int                              frameNumber      = 0;

  int                              textColor        = color(0, 256, 0);

  ControlWindow                    controlWindow;

  public int                       sliderValue      = 40;

  String                           defualtInfo      = "Not Connected, Please Log in to XMPP Server";

  String                           netInfoTabTitle  = "NET INFO";

  Tab                              settingsTab;

  Tab                              netInfoTab;

  Tab                              helpTab;

  SortedMap<String, Button>        rosterMap        = new TreeMap<String, Button>();

  public void setup()
  {
    size(width, height, P2D);
    builder = Joystick.ControllerState.newBuilder();
    background(0);

    // Set the font and its size (in units of pixels)
    fontA = loadFont("../data/CourierNew36.vlw");
    textAlign(CENTER);
    textFont(fontA, 18);
    df = new DecimalFormat("#.#");

    capture = new CaptureMJPEG(this, videoPipe);
    frameRate(20);
    controlP5 = new ControlP5(this);
    email = controlP5.addTextfield("email", 10, 10, 200, 20);
    // email.setValue(defEmail);
    email.setFocus(true);
    msgOutBuffer = new byte[1024];
    capture.startCapture();

    controlWindow = controlP5.addControlWindow("controlP5window", 100, 100, 400, 200);
    controlWindow.setUpdateMode(ControlWindow.ECONOMIC);
    controlWindow.hideCoordinates();
    controlWindow.setTitle("Monarch Info");
    controlWindow.setBackground(color(40));
    // controlWindow.setUndecorated(true);

    netInfoTab = new Tab(controlP5, controlWindow, netInfoTabTitle);
    controlWindow.add(netInfoTab);
    netInfo = controlP5.addTextlabel("netInfo", defualtInfo, 20, 20);
    netInfo.moveTo(netInfoTab);

    settingsTab = new Tab(controlP5, controlWindow, "settings");
    controlWindow.add(settingsTab);

    helpTab = (Tab) controlWindow.tabs().get(0);
    helpTab.setLabel("help");
    helpTab.setStringValue("help");
    controlWindow.removeTab(helpTab);
    controlWindow.add(helpTab);
    controlWindow.activateTab(netInfoTabTitle);
    // load an image ( PNG-8 or JPEG ) as a sprite ( transparancy doesn't seem
    // to work with PNG )
    ControllerSprite dup = new ControllerSprite(controlP5, loadImage("../imgs/d-up.png"), 40, 40, 1);
    ControllerSprite ddown = new ControllerSprite(controlP5, loadImage("../imgs/d-down.png"), 40, 40, 1);
    ControllerSprite dleft = new ControllerSprite(controlP5, loadImage("../imgs/d-left.png"), 40, 40, 1);
    ControllerSprite dright = new ControllerSprite(controlP5, loadImage("../imgs/d-right.png"), 40, 40, 1);

    controlP5.Button move_fwd = controlP5.addButton("move_UP", 123, width - 100, height - 100, 50, 50);
    move_fwd.setSprite(dup);
    controlP5.Button move_bkwd = controlP5.addButton("move_DOWN", 123, width - 100, height - 50, 50, 50);
    move_bkwd.setSprite(ddown);
    controlP5.Button turn_left = controlP5.addButton("move_LEFT", 123, width - 150, height - 50, 50, 50);
    turn_left.setSprite(dleft);
    controlP5.Button turn_right = controlP5.addButton("move_RIGHT", 123, width - 50, height - 50, 50, 50);
    turn_right.setSprite(dright);

    Button rosterRefresh = controlP5.addButton("RefreshRoster", 100);
    rosterRefresh.moveTo(settingsTab);

  }

  private void onRosterUpdate(String line)
  {

    // Removing from roster: itbebeth@gmail.com/gmail.61187190
    // Adding to roster: darrellt@gmail.com/monarch2FB30ACE
    String s[] = line.split("roster: ");
    String jid = s[s.length - 1];
    s = jid.split("@");
    String name = s[0];
    if (line.contains("Adding to roster:"))
    {
      Button b = controlP5.addButton(name, 100);
      b.moveTo(netInfoTab);
      rosterMap.put(jid, b);
    }
    else
    {
      try
      {
        Button b = rosterMap.remove(jid);
        b.remove();
      }
      catch (Exception e)
      {

      }
    }

    Iterator<String> iterator = rosterMap.keySet().iterator();
    float i = 2;
    int space = 30;
    while (iterator.hasNext())
    {
      String key = (String) iterator.next();
      Button b = rosterMap.get(key);
      b.setPosition(space, i * space);
      i++;
    }

    netInfo.setValue("XMPP CONNECTED, " + rosterMap.size() + " callable in roster");
    controlWindow.draw();
    // controlP5.addButton("buttonA", 0, 100, 100, 80, 19);

  }

  private void sendControlMsg(Joystick.ControllerState.Builder b)
  {
    b.setTimestamp(System.currentTimeMillis());
    int size = 0;
    msgOutBuffer = b.build().toByteArray();
    size = msgOutBuffer.length;
    // monarchProtoMsgSendChannel.flush(); // Write the remaining data
    // } catch (IOException e) {
    // TODO Auto-generated catch block
    // e.printStackTrace();
    // }
    String s = "PBUFMSG " + size + " ";
    int i = 0;
    while (i < size)
    {
      s = s + String.format("%02X", msgOutBuffer[i]);
      i++;
    }
    println(s);
    if (monarchXMPPWriter != null)
    {
      monarchXMPPWriter.println(s);
      monarchXMPPWriter.flush();
    }
    b.clear();
  }

  public void send_move(String keycode)
  {

    monarch.Joystick.ControllerState.KeyEvent.Builder keybuilder = monarch.Joystick.ControllerState.KeyEvent.newBuilder();
    keybuilder.setKeyCode(keycode.toUpperCase());
    keybuilder.setKeyDown(true);
    builder.addKeyEvent(keybuilder);
    sendControlMsg(builder);

  }

  public void checkKeyPressed()
  {

    if (keyPressed)
    {

      // println(key);
      monarch.Joystick.ControllerState.KeyEvent.Builder keybuilder = monarch.Joystick.ControllerState.KeyEvent.newBuilder();
      if (key == CODED)
      {
        keybuilder.setKeyDown(true);
        if (keyCode == UP)
        {
          keybuilder.setKeyCode("UP");
        }
        else if (keyCode == DOWN)
        {
          keybuilder.setKeyCode("DOWN");
        }
        else if (keyCode == LEFT)
        {
          keybuilder.setKeyCode("LEFT");
        }
        else if (keyCode == RIGHT)
        {
          keybuilder.setKeyCode("RIGHT");
        }
        else
        {
          return;
        }

        builder.addKeyEvent(keybuilder);
        sendControlMsg(builder);
      }
    }

  }

  private void startMonarch(String em, String pw)
  {
    try
    {

      String cmdString = "killall " + MONARCHEXECUABLE;
      Runtime rt = Runtime.getRuntime();
      rt.exec(cmdString);
      delay(10);

      cmdString = MONARCHEXECUABLE + " " + em + " " + pw;

      if (autoConnect)
      {
        cmdString = cmdString + " -msgsize=123";
      }

      Process proc = rt.exec(cmdString);

      InputStream stderr = proc.getInputStream();
      monarchXMPPWriter = new PrintWriter(proc.getOutputStream(), true);
      InputStreamReader isr = new InputStreamReader(stderr);
      monarchXMPPReader = new BufferedReader(isr);
      // monarchProtoMsgSendChannel = new FileOutputStream(commandPipe);
      background(235);
    }
    catch (Throwable t)
    {
      t.printStackTrace();
    }
  }

  private void connectMonarch(String jid)
  {
    println("controlEvent: connecting to: " + jid);
    // monarchXMPPWriter.println("roster");
    monarchXMPPWriter.println("test " + jid);
    // monarchXMPPWriter.flush();
  }

  public void controlEvent(ControlEvent theEvent)
  {

    //println("controlEvent: accessing a string from controller '" + theEvent.controller().name() + "': " + theEvent.controller().stringValue());

    if (theEvent.controller().name().startsWith("move_"))
    {
      send_move(theEvent.controller().name().substring(5));
    }

    if (theEvent.controller() == rosterRefresh)
    {
      monarchXMPPWriter.println("roster");

    }

    Iterator<String> iterator = rosterMap.keySet().iterator();

    while (iterator.hasNext())
    {
      String key = (String) iterator.next();
      Button b = rosterMap.get(key);
      if (b == theEvent.controller())
      {
        connectMonarch(key);
      }
    }
  }

  public void onXmppConnect()
  {
    // netInfo.setLabel("XMPP CONNECTED");
    netInfo.setValue("XMPP CONNECTED, " + rosterMap.size() + "callable in roster");
    controlWindow.draw();
  }

  public void readMonarch()
  {
    String line = null;
    try
    {
      while (monarchXMPPReader != null && monarchXMPPReader.ready())
      {
        line = monarchXMPPReader.readLine();
        if (line.contains("roster"))
        {
          onRosterUpdate(line);
        }
        if (line.contains("XMPP CONNECTED"))
        {
          onXmppConnect();
        }

        System.out.println(line);
      }
    }
    catch (IOException e)
    {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  /*
   * Jingle:Conn[tcp:0:local:udp:10.0.1.4:55026->tcp:0:local:udp:10.0.1.2:58928|CRW
   * |52]: UpdateState(): pings_since_last_response_=, rtt=104, now=1419775711
   * Jingle
   * :Conn[tcp:0:stun:udp:98.245.36.251:33929->tcp:0:local:udp:10.0.1.2:58928
   * |CR-|-]: UpdateState(): pings_since_last_response_=1419768234 , rtt=3000,
   * now=1419775711
   * Jingle:Conn[tcp:0:local:udp:10.0.1.4:55026->tcp:0:stun:udp:10.0
   * .1.2:65448|CR-|-]: UpdateState(): pings_since_last_response_=, rtt=3000,
   * now=1419775711
   * Jingle:Conn[tcp:0:stun:udp:98.245.36.251:33929->tcp:0:stun:udp
   * :98.245.36.251:34911|CR-|-]: UpdateState(): pings_since_last_response_=,
   * rtt=3000, now=1419775711
   * Jingle:Conn[tcp:0:stun:udp:98.245.36.251:33929->tcp
   * :0:stun:udp:10.0.1.2:65448|CR-|-]: UpdateState():
   * pings_since_last_response_=, rtt=3000, now=1419775711
   * Jingle:Conn[tcp:0:local
   * :udp:10.0.1.4:55026->tcp:0:local:udp:10.0.1.2:58928|CRW|52]: Sending STUN
   * ping 1KfZwLIUXGBp at 1419775716
   */

  public void draw()
  {
    // next_img = capture.getImage();
    if (next_img != null)
    {
      frameNumber++;
      image(next_img, 0, 0, 640, 480);
      color(0, 255, 0);
      String fps = "FPS:" + df.format(capture.getFPS());
      text(fps, 40, 20);
      String size = "Size:" + df.format((float) capture.getFrameSize() / 1000.0) + "kb";
      text(size, 40, 40);
    }
    readMonarch();
    checkKeyPressed();
    controlP5.draw();
  }

  public void captureMJPEGEvent(PImage img)
  {
    next_img = img;
  }

  public void email(String theText)
  {
    if (xmppEmail == null)
    {
      xmppEmail = theText;
      background(0);
      email.setCaptionLabel("password");
      email.setPasswordMode(true);

    }
    else
    {
      xmppPassword = theText;
      email.setFocus(false);
      email.remove();
      startMonarch(xmppEmail, xmppPassword);
    }
  }

  public void dispose()
  {
    // anything in here will be called automatically when
    // the parent applet shuts down. for instance, this might
    // shut down a thread used by this library.

    String cmd = "quit\n";
    monarchXMPPWriter.print(cmd);
  }

}
